package co.edu.udea.sigepi.data.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.naming.InitialContext;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import co.edu.udea.sigepi.data.dao.DAOGenerico;
import co.edu.udea.sigepi.data.dao.exception.DAOException;

/**
 * {@inheritDoc}
 */
public class DAOGenericoImpl<T> implements DAOGenerico<T> {

	protected Class<T> domainClass = extraerClaseDominio();

	protected Transaction transaction = null;

	static Logger logger = null;

	static {
		/**
		 * Clase de logging.
		 */
		logger = Logger.getLogger(DAOGenericoImpl.class);
		// Setea una simple configuracion para la libreria log4j.
		BasicConfigurator.configure();
	}
	
	private final SessionFactory sessionFactory = getSessionFactory();

	protected SessionFactory getSessionFactory() {
		try {
			return (SessionFactory) new InitialContext()
					.lookup("SessionFactory");
		} catch (Exception e) {
			logger.error("No se pudo encontrar el SessionFactory en JNDI", e);
			throw new IllegalStateException(
					"No se pudo encontrar el SessionFactory en JNDI");
		}
	}

	/**
	 * 
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	protected Class<T> extraerClaseDominio() {
		if (domainClass == null) {
			ParameterizedType thisType = (ParameterizedType) getClass()
					.getGenericSuperclass();
			domainClass = (Class<T>) thisType.getActualTypeArguments()[0];
		}
		return domainClass;
	}

	/**
	 * 
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T getById(int id) throws DAOException {
		Session session = sessionFactory.openSession();
		T t = null;
		try {
			t = (T) session.get(extraerClaseDominio(), id);
		} catch (HibernateException e) {
			logger.error("No se pudo obtener el registro de tipo " + t.getClass()
					+ " con Id: " + id, e);
			throw new DAOException(e);
		} finally {
//			DAOUtility
		}
		return t;
	}

	/**
	 * {@inheritDoc}
	 */
	public void guardar(T entity) throws DAOException {
		Session session =  null;	
		Transaction tx = null;
		try{
			session = sessionFactory.openSession();
			tx = session.beginTransaction();
			session.saveOrUpdate(entity);
			tx.commit();
		}catch (HibernateException e){
			tx.rollback();
			logger.error("No se pudo guardar la entidad de tipo " + entity.getClass(), e);
			throw new DAOException(e);
		}
		finally{
			//session.close();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<T> listar(String clausulaWhere) throws DAOException {
		//SCRIPT BUILDER
		StringBuilder sb = new StringBuilder("FROM ");
		sb.append(extraerClaseDominio().getName());
		sb.append(" ");
		sb.append(clausulaWhere);
		//VARIABLES LOCALES
		Session session = sessionFactory.openSession();
		List<T> listat = null;
		try {
			listat = (List<T>) session.createQuery(sb.toString())
					.list();
		} catch (HibernateException e) {
			logger.error("No se pudo obtener la lista de tipo " + extraerClaseDominio().getName(), e);
			throw new DAOException(e);
		} finally {
			//HibernateUtil.closeSession(session);
		}
		return listat;
	}
		
	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T obtenerPorParametro(String clausulaWhere, String posicion, String parametro) throws DAOException {
		//SCRIPT BUILDER
		StringBuilder sb = new StringBuilder("FROM ");
		sb.append(extraerClaseDominio().getName());
		sb.append(" ");
		sb.append(clausulaWhere);
		//VARIABLES LOCALES
		Session session = sessionFactory.openSession();
		Query query = null;
		T t = null;
		try {
			query = session.createQuery(sb.toString());
			query.setString(posicion, parametro);
			t = (T) query.uniqueResult();
		} catch (HibernateException e) {
			logger.error("No se pudo obtener la clase de tipo " + extraerClaseDominio().getName(), e);
			throw new DAOException(e);
		} finally {
			//HibernateUtil.closeSession(session);
		}
		return t;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> listarPorParametro(String clausulaWhere, String posicion, String parametro) throws DAOException {
		//SCRIPT BUILDER
		StringBuilder sb = new StringBuilder("FROM ");
		sb.append(extraerClaseDominio().getName());
		sb.append(" ");
		sb.append(clausulaWhere);
		//VARIABLES LOCALES
		Session session = sessionFactory.openSession();
		Query query = null;
		List<T> lista = null;

		try {
			query = session.createQuery(sb.toString());
			query.setString(posicion, parametro);			
			lista = (List<T>) query.list();
		} catch (HibernateException e) {
			logger.error("No se pudo obtener la clase de tipo " + extraerClaseDominio().getName(), e);
			throw new DAOException(e);
		} finally {
			//HibernateUtil.closeSession(session);
		}
		
		return lista;
	}
		
	@SuppressWarnings("unchecked")
	public List<T> listarPorParametroNumerico(String clausulaWhere, String posicion, int parametro) throws DAOException {
		
		//SCRIPT BUILDER
		StringBuilder sb = new StringBuilder("FROM ");
		sb.append(extraerClaseDominio().getName());
		sb.append(" ");
		sb.append(clausulaWhere);
		//VARIABLES LOCALES
		Session session = sessionFactory.openSession();
		Query query = null;
		List<T> lista = null;
		
		try {
			query = session.createQuery(sb.toString());//Aqui falla
			query.setInteger(posicion, parametro);		
			lista = (List<T>)query.list();
		} catch (HibernateException e) {
			logger.error("No se pudo obtener la clase de tipo " + extraerClaseDominio().getName(), e);
			throw new DAOException(e);
		} finally {
			//HibernateUtil.closeSession(session);
		}
		
		return lista;
	}
}